#pragma once

template <class type>
inline type implicit_cast( type obj )
{
	return obj;
}

class NtCountedObject
{
public:
	NtCountedObject();
	NtCountedObject( const NtCountedObject& arg );
	virtual ~NtCountedObject();

	NtCountedObject& operator=( NtCountedObject const& arg );
	int get_reference_count() const;
private:
	friend class NtCountedPtrBase;

	void add_reference() const;
	void remove_reference() const;
	mutable int m_reference_count;
};

inline NtCountedObject::NtCountedObject()
{
	m_reference_count = 0;
}

inline NtCountedObject::NtCountedObject( NtCountedObject const& )
{
	m_reference_count = 0;
}

inline NtCountedObject::~NtCountedObject()
{
}

inline NtCountedObject& NtCountedObject::operator =( 
	NtCountedObject const& arg )
{
	return *this;
}

inline void NtCountedObject::add_reference() const
{
	if (this)
		m_reference_count++;
}

inline int NtCountedObject::get_reference_count() const
{
	return m_reference_count;
}

inline void NtCountedObject::remove_reference() const
{
	if (this && --m_reference_count == 0)
		delete this;
}

class NtCountedPtrBase
{
public:
	NtCountedPtrBase();
	NtCountedPtrBase( NtCountedObject const * object );
	NtCountedPtrBase( NtCountedPtrBase const& pointer );
	~NtCountedPtrBase();

	bool operator==( NtCountedObject const* arg ) const;
	bool operator!=( NtCountedObject const* arg ) const;
	bool operator==( NtCountedPtrBase const& arg ) const;
	bool operator!=( NtCountedPtrBase const& arg ) const;
	bool operator!() const;

	bool operator<(NtCountedPtrBase const& arg ) const; 
	bool operator<=(NtCountedPtrBase const& arg ) const; 
	bool operator>(NtCountedPtrBase const& arg ) const; 
	bool operator>=(NtCountedPtrBase const& arg ) const; 

	NtCountedPtrBase& operator=( NtCountedObject const * object );
	NtCountedPtrBase& operator=( NtCountedPtrBase const& arg );
protected:
	NtCountedObject const * m_object;
};

inline NtCountedPtrBase::NtCountedPtrBase()
{
	m_object = 0;
}

inline NtCountedPtrBase::NtCountedPtrBase( NtCountedObject const * object )
{
	m_object = object;
	object->add_reference();
}

inline NtCountedPtrBase::NtCountedPtrBase( NtCountedPtrBase const& pointer )
{
	m_object = pointer.m_object;
	m_object->add_reference();
}

inline NtCountedPtrBase::~NtCountedPtrBase()
{
	m_object->remove_reference();
}


inline bool NtCountedPtrBase::operator==( NtCountedObject const* arg ) const
{ 
	return m_object == arg; 
}

inline bool NtCountedPtrBase::operator!=( NtCountedObject const* arg ) const
{
	return m_object != arg; 
}

inline bool NtCountedPtrBase::operator==( NtCountedPtrBase const& arg ) const
{
	return m_object == arg.m_object;
}

inline bool NtCountedPtrBase::operator!=( NtCountedPtrBase const& arg ) const
{
	return m_object != arg.m_object;
}

inline bool NtCountedPtrBase::operator!() const
{
	return m_object == 0;
}

inline NtCountedPtrBase& NtCountedPtrBase::operator=( 
	NtCountedObject const * object )
{
	object->add_reference();
	m_object->remove_reference();
	m_object = object;
	return *this;
}

inline NtCountedPtrBase& NtCountedPtrBase::operator=( 
	NtCountedPtrBase const& arg )
{
	NtCountedObject* object = const_cast<NtCountedObject*>(arg.m_object);

	object->add_reference();
	m_object->remove_reference();
	m_object = object;
	return *this;
}

inline
bool NtCountedPtrBase::operator<(NtCountedPtrBase const& arg ) const
{
	return m_object < arg.m_object;
}

inline
bool NtCountedPtrBase::operator<=(NtCountedPtrBase const& arg ) const
{
	return m_object <= arg.m_object;
}

inline
bool NtCountedPtrBase::operator>(NtCountedPtrBase const& arg ) const
{
	return m_object > arg.m_object;
}

inline
bool NtCountedPtrBase::operator>=(NtCountedPtrBase const& arg ) const
{
	return m_object >= arg.m_object;
}

template<class type> 
class NtCountedPtr : public NtCountedPtrBase
{
public:
	typedef type element_type;

	NtCountedPtr();
	NtCountedPtr( type* object );

	template <class other_type>
	NtCountedPtr( NtCountedPtr<other_type> const& pointer )
		: NtCountedPtrBase( implicit_cast<type*>( pointer.get() ) )
	{
	}

	NtCountedPtr( NtCountedPtr const& pointer );

	type*       get() const;

	NtCountedPtr& operator=( type* arg );

	template < class t_other_type >
	NtCountedPtr& operator=( NtCountedPtr< t_other_type > const& arg )
	{
		return static_cast<NtCountedPtr<type>&>(NtCountedPtrBase::operator=
			( implicit_cast<element_type *>( arg.get() )));
	}

	NtCountedPtr& operator=( NtCountedPtr const& arg );
	operator type* () const;
	type*          operator->() const;
	type&          operator*() const;
};

template <class type>
inline NtCountedPtr<type>::NtCountedPtr() 
: NtCountedPtrBase() 
{
}

template <class type>
inline NtCountedPtr<type>::NtCountedPtr( type* object ) 
: NtCountedPtrBase( object ) 
{
}

template <class type>
inline NtCountedPtr<type>::NtCountedPtr( const NtCountedPtr& pointer ) 
: NtCountedPtrBase( pointer ) 
{
}

template <class type>
inline NtCountedPtr<type>& NtCountedPtr<type>::operator=( type* arg )
{
	return static_cast<NtCountedPtr<type>&>(
		NtCountedPtrBase::operator=(arg)); 
}

template <class type>
inline type* NtCountedPtr<type>::get() const
{ 
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline NtCountedPtr<type>::operator type* () const
{
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline type* NtCountedPtr<type>::operator->() const
{
	return const_cast<type*>( static_cast< type const * >(m_object) ); 
}

template <class type>
inline type& NtCountedPtr<type>::operator*() const
{ 
	return const_cast<type&>( static_cast< type const & >( *m_object) ); 
}

template <class type>
inline NtCountedPtr<type>& NtCountedPtr<type>::operator=(
	const NtCountedPtr<type>& arg )
{ 
	return static_cast<NtCountedPtr<type>&>(NtCountedPtrBase::operator=(arg)); 
}

template<class t_type> 
class NtCountedConstPtr : public NtCountedPtrBase
{
public:
	typedef t_type const t_element_type;

	NtCountedConstPtr();
	NtCountedConstPtr( t_element_type * object );

	template <class other_type>
	explicit NtCountedConstPtr( NtCountedConstPtr<other_type> 
		const& pointer ): NtCountedPtrBase( implicit_cast<t_element_type*>
		( pointer.get() ) )
	{
	}

	NtCountedConstPtr( NtCountedConstPtr const& pointer );
	NtCountedConstPtr( NtCountedPtr<t_type> const& pointer );

	t_element_type* get() const;

	NtCountedConstPtr& operator=( t_element_type* arg );

	template < class t_other_type >
	NtCountedConstPtr& operator=( NtCountedConstPtr< t_other_type >
		const& arg )
	{
		return static_cast<NtCountedConstPtr<t_type>&>
			(NtCountedPtrBase::operator=(implicit_cast<t_element_type *>
			( arg.get() ) ) ); 
	}

	template < class t_other_type >
	NtCountedConstPtr& operator=( NtCountedPtr< t_other_type > const& arg )
	{
		return static_cast<NtCountedConstPtr<t_type>&>
			(NtCountedPtrBase::operator=(implicit_cast<t_element_type *>
			( arg.get() ) ) ); 
	}

	NtCountedConstPtr& operator=( NtCountedConstPtr const& arg );
	NtCountedConstPtr& operator=( NtCountedPtr<t_type> const& arg );
	operator t_element_type* () const;
	t_element_type*      operator->() const;
	t_element_type&      operator*() const;
};

template <class t_type>
inline NtCountedConstPtr<t_type>::NtCountedConstPtr() : NtCountedPtrBase() 
{
}

template <class t_type>
inline NtCountedConstPtr<t_type>::NtCountedConstPtr( t_element_type* object ) 
: NtCountedPtrBase( object ) 
{
}

template <class t_type>
inline NtCountedConstPtr<t_type>::NtCountedConstPtr( 
	NtCountedConstPtr const& pointer ) 
	: NtCountedPtrBase( pointer ) 
{
}

template <class t_type>
inline NtCountedConstPtr<t_type>::NtCountedConstPtr( 
	NtCountedPtr<t_type> const& pointer )
	: NtCountedPtrBase( pointer )
{
}

template <class t_type>
inline NtCountedConstPtr<t_type>& NtCountedConstPtr<t_type>::operator=(
	t_element_type * arg )
{
	return static_cast<NtCountedConstPtr<t_type>&>(
		NtCountedPtrBase::operator=(arg)); 
}

template <class t_type>
inline t_type const * NtCountedConstPtr<t_type>::get() const
{ 
	return static_cast<t_element_type*>(m_object); 
}

template <class t_type>
inline NtCountedConstPtr<t_type>::operator t_type const *() const
{
	return static_cast<t_element_type*>(m_object); 
}

template <class t_type>
inline t_type const * NtCountedConstPtr<t_type>::operator->() const
{
	return static_cast<t_element_type*>(m_object);
}

template <class t_type>
inline t_type const & NtCountedConstPtr<t_type>::operator*() const
{ 
	return static_cast<t_element_type&>(*m_object);
}

template < class t_type >
NtCountedConstPtr<t_type>& NtCountedConstPtr<t_type>::operator=(
	NtCountedConstPtr<t_type> const& arg )
{
	return static_cast<NtCountedConstPtr<t_type>&>( 
		NtCountedPtrBase::operator=( arg )); 
}

template < class t_type >
NtCountedConstPtr<t_type>& NtCountedConstPtr<t_type>::operator=( 
	NtCountedPtr<t_type> const& arg )
{
	return static_cast<NtCountedConstPtr<t_type>&>(
		NtCountedPtrBase::operator=( arg ) ); 
}

#define NtAutoPtr(ClassName) typedef NtCountedPtr<ClassName> ClassName##Ptr

#define NtSmartPointerCast(ClassName, Object) \
	(Object) == NULL ? NULL : (ClassName*)( (Object).get())
